"use strict";

/**
 * Example JavaScript code that interacts with the page and Web3 wallets
 */

// Unpkg imports
const Web3Modal = window.Web3Modal.default;
const WalletConnectProvider = window.WalletConnectProvider.default;
const Fortmatic = window.Fortmatic;
const evmChains = window.evmChains;

const chains = {
  rinkeby: {
    chain_id: 4,
    chain_name: 'Ethereum Rinkeby',
    chain_id_hex: '0x4',
    contract: '../contract/rinkeby.json',
    scan: 'https://rinkeby.etherscan.io',
    rpc: 'https://rinkeby.infura.io/v3/0d09d45a079d40528ba319ed3b3e8020',
    fortmatic_key: "pk_test_391E26A3B43A3350",
    infuraId: "0d09d45a079d40528ba319ed3b3e8020",
  },
  mainnet: {
    chain_id: 1,
    chain_name: 'Ethereum Mainnet',
    chain_id_hex: '0x1',
    contract: '../contract/mainnet.json',
    scan: 'https://etherscan.io',
    rpc: 'https://mainnet.infura.io/v3/0d09d45a079d40528ba319ed3b3e8020',
    fortmatic_key: "pk_live_7FAEFCAFD27B1B36",
    infuraId: "0d09d45a079d40528ba319ed3b3e8020",
  },
};
let app_chain_id = 1;
let user_chain_id = 0;
let controllerV1;
let rnd;
let boyToken;
let horseToken;
let armsToken;
let account_balance;
let stakingRND = ethers.BigNumber.from('450000000000000000000000000');
// Web3modal instance
let web3Modal

// Chosen wallet provider given by the dialog window
let provider;


// Address of the selected account
let selectedAccount;

function getChainInfo() {
  switch (app_chain_id) {
    case 4:
      return chains.rinkeby;
    case 1:
      return chains.mainnet;
  }
}


/**
 * Setup the orchestra
 */
function init() {

  console.log("Initializing example");
  console.log("WalletConnectProvider is", WalletConnectProvider);
  console.log("Fortmatic is", Fortmatic);
  console.log("window.web3 is", window.web3, "window.ethereum is", window.ethereum);

  // Check that the web page is run in a secure context,
  // as otherwise MetaMask won't be available
  //if(location.protocol !== 'https:') {
  // https://ethereum.stackexchange.com/a/62217/620
  // const alert = document.querySelector("#alert-error-https");
  // alert.style.display = "block";
  // document.querySelector("#btn-connect").setAttribute("disabled", "disabled")
  // return;
  //}

  // Tell Web3modal what providers we have available.
  // Built-in web browser provider (only one can exist as a time)
  // like MetaMask, Brave or Opera is added automatically by Web3modal
  const providerOptions = {
    walletconnect: {
      package: WalletConnectProvider,
      options: {
        rpc: {
          1: getChainInfo().rpc,
          4: getChainInfo().rpc,
        },
        // Mikko's test key - don't copy as your mileage may vary
        infuraId: getChainInfo().infuraId,
      }
    },
    fortmatic: {
      package: Fortmatic,
      options: {
        key: getChainInfo().fortmatic_key
      }
    }
  };

  web3Modal = new Web3Modal({
    cacheProvider: false, // optional
    providerOptions, // required
    disableInjectedProvider: false, // optional. For MetaMask / Brave / Opera.
  });

  console.log("Web3Modal instance is", web3Modal);
}


function readTextFile(file, callback) {
  var rawFile = new XMLHttpRequest();
  rawFile.overrideMimeType("application/json");
  rawFile.open("GET", file, true);
  rawFile.onreadystatechange = function () {
    if (rawFile.readyState === 4 && rawFile.status == "200") {
      callback(rawFile.responseText);
    }
  }
  rawFile.send(null);
}

/**
 * Kick in the UI action after Web3modal dialog has chosen a provider
 */
async function fetchAccountData() {

  // Get a Web3 instance for the wallet
  const web3 = new Web3(provider);

  console.log("Web3 instance is", web3);

  // Get connected chain id from Ethereum node
  const chainId = await web3.eth.getChainId();
  // Load chain information over an HTTP API
  const chainData = evmChains.getChain(chainId);
  document.querySelector("#network-name").textContent = chainData.name;

  // Get list of accounts of the connected wallet
  const accounts = await web3.eth.getAccounts();

  // MetaMask does not give you all accounts, only the selected account
  console.log("Got accounts", accounts);
  selectedAccount = accounts[0];

  document.querySelector("#selected-account").textContent = selectedAccount;

  // Get a handl
  const template = document.querySelector("#template-balance");
  const accountContainer = document.querySelector("#accounts");

  // Purge UI elements any previously loaded accounts
  accountContainer.innerHTML = '';

  // Go through all accounts and get their ETH balance
  const rowResolvers = accounts.map(async (address) => {
    const balance = await web3.eth.getBalance(address);
    // ethBalance is a BigNumber instance
    // https://github.com/indutny/bn.js/
    const ethBalance = web3.utils.fromWei(balance, "ether");
    const humanFriendlyBalance = parseFloat(ethBalance).toFixed(4);
    // Fill in the templated row and put in the document
    const clone = template.content.cloneNode(true);
    clone.querySelector(".address").textContent = address;
    clone.querySelector(".balance").textContent = humanFriendlyBalance;
    accountContainer.appendChild(clone);


  });

  // Because rendering account does its own RPC commucation
  // with Ethereum node, we do not want to display any results
  // until data for all accounts is loaded
  await Promise.all(rowResolvers);

  // Display fully loaded UI for wallet data
  document.querySelector("#prepare").style.display = "none";
  document.querySelector("#connected").style.display = "block";


  document.querySelector("#btn-mint").removeAttribute("disabled");
  document.querySelector("#btn-mint2").removeAttribute("disabled");

  // Display fully loaded UI for wallet data
  const provider1 = new ethers.providers.Web3Provider(web3.currentProvider);
  var signer = provider1.getSigner();
  // console.log("Web3Modal instance is", web3Modal);
  readTextFile(getChainInfo().contract, function (text) {
    var json = JSON.parse(text);
    console.log(json);
    controllerV1 = new ethers.Contract(json.ControllerV1.address, json.ControllerV1.abi, signer);
    rnd = new ethers.Contract(json.RND.address, json.RND.abi, signer);
    boyToken = new ethers.Contract(json.BoyToken.address, json.BoyToken.abi, signer);
    horseToken = new ethers.Contract(json.HorseToken.address, json.HorseToken.abi, signer);
    armsToken = new ethers.Contract(json.ArmsToken.address, json.ArmsToken.abi, signer);
    refreshCanMint();
    refreshFreeMintDate();

  });
}

async function refreshCanMint() {

  const maxMintCount1 = await controllerV1.getMintLimit(0);
  const balanceOf1 = await boyToken.balanceOf(selectedAccount);
  const canMintCount1 = maxMintCount1 - balanceOf1;
  document.querySelector("#canMintCount1").innerText = "You can mint " + canMintCount1;
  document.querySelector("#mintCount").max = canMintCount1;

  const maxMintCount2 = await controllerV1.getMintLimit(1);
  const balanceOf2 = await horseToken.balanceOf(selectedAccount);
  const canMintCount2 = maxMintCount2 - balanceOf2;
  document.querySelector("#canMintCount2").innerText = "You can mint " + canMintCount2;
  document.querySelector("#mintCount2").max = canMintCount2;

}

async function refreshFreeMintDate() {
  const freeMintEndDate = await controllerV1.getFreeMintEndDate();
  document.querySelector('#freeMintDate').value = freeMintEndDate;
}

/**
 * Fetch account data for UI when
 * - User switches accounts in wallet
 * - User switches networks in wallet
 * - User connects wallet initially
 */
async function refreshAccountData() {

  // If any current data is displayed when
  // the user is switching acounts in the wallet
  // immediate hide this data
  document.querySelector("#connected").style.display = "none";
  document.querySelector("#prepare").style.display = "block";

  // Disable button while UI is loading.
  // fetchAccountData() will take a while as it communicates
  // with Ethereum node via JSON-RPC and loads chain data
  // over an API call.
  document.querySelector("#btn-connect").setAttribute("disabled", "disabled")
  await fetchAccountData(provider);
  document.querySelector("#btn-connect").removeAttribute("disabled")

}


/**
 * Connect wallet button pressed.
 */
async function onConnect() {

  console.log("Opening a dialog", web3Modal);
  try {
    provider = await web3Modal.connect();
  } catch (e) {
    console.log("Could not get a wallet connection", e);
    return;
  }

  // Subscribe to accounts change
  provider.on("accountsChanged", (accounts) => {
    fetchAccountData();
  });

  // Subscribe to chainId change
  provider.on("chainChanged", (chainId) => {
    fetchAccountData();
  });

  // Subscribe to networkId change
  provider.on("networkChanged", (networkId) => {
    fetchAccountData();
  });

  await refreshAccountData();
}


/**
 * mint SagittariusBoy button pressed.
 */
async function onMint() {
  document.querySelector("#btn-mint").innerText = "minting...";
  const mintCount = ethers.BigNumber.from(document.querySelector("#mintCount").value);
  const recommenderAddress = document.querySelector("#recommenderAddress").value;

  const balanceOf = await boyToken.balanceOf(selectedAccount);
  const maxMintCount = await controllerV1.getMintLimit(0);
  const canMitCount = maxMintCount - balanceOf;
  if (mintCount > canMitCount) {
    alert("You can mint " + canMitCount);
    return;
  }
  const freeMintCount = await controllerV1.getFreeMintCount(selectedAccount);
  if (freeMintCount < mintCount) {
    const total = mintCount.mul(stakingRND);
    const balance = await rnd.balanceOf(selectedAccount);
    if (total.gt(balance)) {
      alert("Insufficient balance, please recharge RND");
      document.querySelector("#btn-mint").innerText = "mint";
      return;
    }
    try {
      const allowanceAmount = await rnd.allowance(selectedAccount, controllerV1.address);
      if (total.gt(allowanceAmount)) {
        let popCount = 0;
        rnd.on("Approval", (owner, spender, value) => {
          console.log(owner, spender, value);
          if (popCount == 0 && owner == selectedAccount && spender == controllerV1.address && value._hex == total._hex) {
            popCount++;
            const mintPromise = controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
              console.log("res2", res2);
              const scanLink = getChainInfo().scan + "/tx/" + res2.hash;
              document.querySelector("#mintResult").innerHTML = '<a target="_blank" href="' + scanLink + '"><h1>View transaction</h1></a>';
              document.querySelector("#btn-mint").innerText = "mint";
            });
          }
        });
        const res1 = await rnd.approve(controllerV1.address, total);
        console.log('res1', res1);
        if (!res1 || !res1.hash) {
          alert("approve fail");
          document.querySelector("#btn-mint").innerText = "mint";
  
        }
      } else {
        const mintPromise = controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
          console.log("res2", res2);
          const scanLink = getChainInfo().scan + "/tx/" + res2.hash;
          document.querySelector("#mintResult").innerHTML = '<a target="_blank" href="' + scanLink + '"><h1>View transaction</h1></a>';
          document.querySelector("#btn-mint").innerText = "mint";
        });
      }
     
    }
    catch (e) {
      console.log(e);
      document.querySelector("#btn-mint").innerText = "mint";
      alert("fail");
    }
  } else {
    if (confirm("You can free mint")) {
      document.querySelector("#btn-mint").innerText = "freeMint";
      const mintPromise = controllerV1.mintBoy(recommenderAddress, mintCount).then(res2 => {
        console.log("res2", res2);
        const scanLink = getChainInfo().scan + "/tx/" + res2.hash;
        document.querySelector("#mintResult").innerHTML = '<a target="_blank" href="' + scanLink + '"><h1>View transaction</h1></a>';
        document.querySelector("#btn-mint").innerText = "mint";
      });
    }
  }
}


/**
 * mint Horse and Arms button pressed.
 */
async function onMint2() {
  document.querySelector("#btn-mint2").innerText = "minting...";
  const mintCount = ethers.BigNumber.from(document.querySelector("#mintCount2").value);
  const price = await controllerV1.getPrice(1);
  const payAmount = mintCount.mul(price);
  const provider = ethers.getDefaultProvider();
  const gasPrice = await provider.getGasPrice();
  const overrides = {
    value: payAmount,
  };
  const res = await controllerV1.mintHorseAndArms(mintCount, overrides);
  console.log("res", res);
  const scanLink = getChainInfo().scan + "/tx/" + res.hash;
  document.querySelector("#mintResult2").innerHTML = '<a target="_blank" href="' + scanLink + '"><h1>View transaction</h1></a>';
  document.querySelector("#btn-mint2").innerText = "mint";
}
async function switchNetwork() {
  try {

    await window.ethereum.request({
      method: 'wallet_switchEthereumChain',
      params: [{ chainId: getChainInfo().chain_id_hex }]
    });
  } catch (err) {
    // This error code indicates that the chain has not been added to MetaMask
    if (err.code === 4902) {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [
          {
            chainName: getChainInfo().chain_name,
            chainId: getChainInfo().chain_id_hex,
            nativeCurrency: { name: 'ETH', decimals: 18, symbol: 'ETH' },
            rpcUrls: [getChainInfo().rpc]
          }
        ]
      });
    }
  }
}

async function onSetFreeMintDate() {
  const freeMintDate = document.querySelector("#freeMintDate").value;
  await controllerV1.changeFreeMintEndDate(freeMintDate);
}


async function onSetFreeMintAddress() {
  const freeMintAddress = document.querySelector("#freeMintAddress").value;
  const allowFreeMintCount = parseInt(document.querySelector("#allowFreeMintCount").value);

  var addrs = freeMintAddress.split(",");
  console.log(addrs);
  await controllerV1.addFreeMint(addrs, allowFreeMintCount);
}

/**
 * Disconnect wallet button pressed.
 */
async function onDisconnect() {

  console.log("Killing the wallet connection", provider);

  // TODO: Which providers have close method?
  if (provider.close) {
    await provider.close();

    // If the cached provider is not cleared,
    // WalletConnect will default to the existing session
    // and does not allow to re-scan the QR code with a new wallet.
    // Depending on your use case you may want or want not his behavir.
    await web3Modal.clearCachedProvider();
    provider = null;
  }

  selectedAccount = null;

  // Set the UI back to the initial state
  document.querySelector("#prepare").style.display = "block";
  document.querySelector("#connected").style.display = "none";
}

async function onFreeMintAddressChanged() {
  const freeMintAddress = document.querySelector("#freeMintAddress").value;
  const addrs = freeMintAddress.split(",");
  document.querySelector("#splitFreeMintAddress").innerHTML = "";
  let html = "";
  for (var i = 0; i < addrs.length; i++) {
    const freeCount = await controllerV1.getFreeMintCount(addrs[i]);
    html += '<span>' + addrs[i] + ':' + freeCount;
  }
  document.querySelector("#splitFreeMintAddress").innerHTML = html;

}
/**
 * Main entry point.
 */
window.addEventListener('load', async () => {
  init();
  document.querySelector("#btn-connect").addEventListener("click", onConnect);
  document.querySelector("#btn-mint").addEventListener("click", onMint);
  document.querySelector("#btn-mint2").addEventListener("click", onMint2);
  document.querySelector("#btn-disconnect").addEventListener("click", onDisconnect);
  document.querySelector("#btn-setFreeMintDate").addEventListener("click", onSetFreeMintDate);
  document.querySelector("#btn-setFreeMintAddress").addEventListener("click", onSetFreeMintAddress);
  document.querySelector("#freeMintAddress").addEventListener("change", onFreeMintAddressChanged);


});
